/*
 * AdditiveFitnessFunction.h
 *
 *  Created on: Jan 15, 2011
 *      Author: LyonsDesktop
 */

#ifndef ADDITIVEFITNESSFUNCTION_H_
#define ADDITIVEFITNESSFUNCTION_H_

#include "Interestingness.h"
#include "../../../Datasets/Dataset.h"
//#include "../../../Extern.h"

#include <vector>
#include <hash_map>
#include <omp.h>

using namespace CLEVER::Datasets;

namespace CLEVER {
namespace RegionDiscovery {
namespace Fitness {
namespace Additive {
//! Calculates the fitness of the given dataset, optionally using indexing or split datasets.
template<typename T>
class AdditiveFitnessFunction
{
public:
	double beta;
	Interestingness<T> * interestingness;
	int cluster_attribute;
	std::vector<Dataset<T> > dataset_array;
	std::vector<DoubleArrayDataset> doubleArrayDataset_array;

	AdditiveFitnessFunction(double beta,
		Interestingness<T> * interestingness,
		int cluster_attribute)
		: beta(beta), interestingness(interestingness), cluster_attribute(cluster_attribute)
	{ }

	//! Precomputers parameters within the function, optionally splitting the dataset if not using indexing.
	virtual void Precompute(Dataset<T> * rD, bool UseIndex = false)
	{                   //PROFILE_FUNC
		interestingness->Precompute(rD);
		if (!UseIndex) {
			dataset_array = rD->Split(cluster_attribute);
		} else {
			dataset_array.clear();
		}
	}


	//! Precomputers parameters within the function, optionally splitting the dataset if not using indexing.
	virtual void Precompute(std::vector<double> & rD)
	{                   //PROFILE_FUNC
		interestingness->Precompute(rD);
		dataset_array.clear();

	}


	virtual void Precompute(DoubleArrayDataset * rD, bool UseIndex = false)
	{                   //PROFILE_FUNC
		interestingness->Precompute(rD);
		if (!UseIndex) {
			dataset_array = rD->Split(cluster_attribute);
		} else {
			dataset_array.clear();
		}
	}


	//! Calculates the fitness of the given dataset using the result of Precompute
	virtual double Fitness(NumericDataset * repSet)
	{                   //PROFILE_FUNC
		return Fitness(dataset_array, repSet);
	}


	//! Calculates the fitness of the given dataset and att as the cluster meta index.
	virtual double Fitness(Dataset<T> * rD, int att, NumericDataset * repSet)
	{
		int nClass = rD->MetaData.attributes[att].Keys.size();
		double f = 0;

		std::vector<int> c_arr;
		c_arr.reserve(nClass);
		std::vector<double> ft_arr;
		for (int i = 0; i < nClass; i++) {
			ft_arr.push_back(0);
			c_arr.push_back(0);
		}

		int size = rD->Size();
		for (int j = 0; j < size; ++j) {
			++c_arr[(int)rD->GetMeta(j, att)];
		}
		///TEST
		//						for (int j = 0; j < nClass; ++j)
		//						{
		//							//std::cout << "c_arr" << j << "=" << c_arr[j] << std::endl;
		//						}
		//#ifndef FRAMEWORKPROFILER
		//	#pragma omp parallel for schedule(dynamic, 1)
#pragma omp parallel for
		//#endif
		for (int i = 0; i < nClass; i++) {
			ft_arr[i] = interestingness->Fitness(rD, repSet, att, i);
			//std::cout << "ft_arr " << ft_arr[i] << std::endl;
		}

		for (int i = 0; i < nClass; i++) {
			f += pow(c_arr[i], beta) * ft_arr[i];
		}

		return f;
	}


	//! Calculates the fitness of the given dataset and att as the cluster meta index.
	virtual double Fitness(std::vector<double> & rD, NumericDataset * gData, int att, NumericDataset * repSet)
	{
		int nClass = repSet->Size();
		double f = 0;

		std::vector<int> c_arr;
		c_arr.reserve(nClass);
		std::vector<double> ft_arr;
		for (int i = 0; i < nClass; i++) {
			ft_arr.push_back(0);
			c_arr.push_back(0);
		}

		int size = rD.size();
		for (int j = 0; j < size; ++j) {
			++c_arr[(int)rD[j]];
		}
		/// TEST
		for (int j = 0; j < nClass; ++j) {
			//std::cout << "c_arr" << j << "=" << c_arr[j] << std::endl;
		}
		for (int i = 0; i < nClass; i++) {
			ft_arr[i] = interestingness->Fitness(rD, gData, repSet, att, i);
			//std::cout << "ft_arr " << ft_arr[i] << std::endl;
		}

		for (int i = 0; i < nClass; i++) {
			f += pow(c_arr[i], beta) * ft_arr[i];
		}

		return f;
	}


	virtual double Fitness(DoubleArrayDataset * rD, int att,  NumericDataset * repSet)
	{
		int nClass = rD->MetaData.attributes[att].Keys.size();
		double f = 0;

		std::vector<int> c_arr;
		c_arr.reserve(nClass);
		std::vector<double> ft_arr;
		for (int i = 0; i < nClass; i++) {
			ft_arr.push_back(0);
			c_arr.push_back(0);
		}

		int size = rD->Size();
		for (int j = 0; j < size; ++j) {
			++c_arr[(int)rD->GetMeta(j, att)];
		}

		//#ifndef FRAMEWORKPROFILER
		//	#pragma omp parallel for schedule(dynamic, 1)
		//#pragma omp parallel for
		//#endif
		for (int i = 0; i < nClass; i++) {
			//std::cout<<"NumericDataset repSet size="<<repSet->Size()<<std::endl;
			ft_arr[i] = interestingness->Fitness(rD, repSet, att, i);
		}

		for (int i = 0; i < nClass; i++) {
			f += pow(c_arr[i], beta) * ft_arr[i];
		}

		return f;
	}


	//! Calculates the fitness of an array of datasets
	virtual double Fitness(std::vector<Dataset<T> > & arr, NumericDataset * repSet)
	{                   //PROFILE_FUNC
		double f = 0;

		for (unsigned int i = 0; i < arr.size(); ++i) {
			double ft = interestingness->Fitness(&arr[i], repSet);
			f += pow(dataset_array[i].Size(), beta) * ft;
		}

		return f;
	}


	virtual double Fitness(std::vector<DoubleArrayDataset > & arr,  NumericDataset * repSet)
	{                   //PROFILE_FUNC
		double f = 0;

		for (unsigned int i = 0; i < arr.size(); ++i) {
			double ft = interestingness->Fitness(&arr[i], repSet);
			f += pow(dataset_array[i].Size(), beta) * ft;
		}

		return f;
	}


};
}
}
}
}

#endif /* ADDITIVEFITNESSFUNCTION_H_ */
